#include "sys_config.h"
#include "typesdef.h"
#include "osal/string.h"
#include "osal/semaphore.h"
#include "osal/mutex.h"
#include "osal/irq.h"
#include "osal/task.h"
#include "osal/sleep.h"
#include "osal/timer.h"
#include "osal/work.h"
#include "mad.h"
#include "mp3_decode.h"

#include "custom_mem/custom_mem.h"
#include "stream_frame.h"

#define BUFSIZE (4096)


static uint32_t get_mp3_data_len(void *data)
{
    struct data_structure  *d = (struct data_structure  *)data;
	return ((uint32_t)d->priv);
}


static uint32_t set_mp3_data_len(void *data,uint32_t len)
{
	struct data_structure  *d = (struct data_structure  *)data;
	d->priv = (void*)len;
	return (uint32_t)len;
}


static stream_ops_func stream_sound_ops = 
{
	.get_data_len = get_mp3_data_len,
	.set_data_len = set_mp3_data_len,
    // .set_data_time = set_sound_data_time,
};


static int opcode_func(stream *s,void *priv,int opcode)
{
	int res = 0;
	switch(opcode)
	{
		case STREAM_OPEN_ENTER:
		break;
		case STREAM_OPEN_EXIT:
		{
            stream_data_dis_mem_custom(s);
			streamSrc_bind_streamDest(s,R_SPEAKER);
            s->priv = priv;
		}
		break;
		case STREAM_OPEN_FAIL:
		break;
		case STREAM_DATA_DIS:
		{
			struct data_structure *data = (struct data_structure *)priv;
			int data_num = (int)data->priv;
			data->type = SET_DATA_TYPE(SOUND,SOUND_MP3);;//设置声音的类型
            data->priv = (void*)(1152*2);
			//注册对应函数
			data->ops = &stream_sound_ops;
            //申请最大的音频size
			data->data = (void*)custom_malloc_psram((1152*2));
		}
		break;

		case STREAM_DATA_DESTORY:
		{
            struct data_structure *data = (struct data_structure *)priv;
            if(data->data)
            {
                custom_free_psram(data->data);
            }
		}
		break;

		//如果释放空间,则删除所有的节点
		case STREAM_DATA_FREE:
		{
		}
		break;

        case STREAM_DATA_FREE_END:
        break;

		//每次即将发送到一个流,就调用一次
		case STREAM_SEND_TO_DEST:
		{

		}
		break;


		//数据发送完成
		case STREAM_SEND_DATA_FINISH:
		{

		}
		break;

		//退出,则关闭对应得流
		case STREAM_CLOSE_EXIT:
		{			
		}
		break;

        //这里移除priv(流名称动态申请)
        case STREAM_DEL:
        {
            if(s->priv)
            {
                custom_free_psram(s->priv);
            }
            
        }

		default:
			//默认都返回成功
		break;
	}
	return res;
}





struct buffer {
  void  *fp;                    /*file pointer*/
  stream *s;
  unsigned int  flen;           /*file length*/
  unsigned int  fpos;           /*current position*/
  unsigned char fbuf[BUFSIZE];  /*buffer*/
  unsigned int  fbsize;         /*indeed size of buffer*/
  uint8_t *file_buf;
  struct extern_read *mad_read;
};

typedef struct buffer mp3_file;


void *pcm_fp = NULL;

/*
 * This is perhaps the simplest example use of the MAD high-level API.
 * Standard input is mapped into memory via mmap(), then the high-level API
 * is invoked with three callbacks: input, output, and error. The output
 * callback converts MAD's high-resolution PCM samples to 16 bits, then
 * writes them to standard output in little-endian, stereo-interleaved
 * format.
 */

//static int decode(unsigned char const *, unsigned long);
static int decode(mp3_file *mp3fp);


//这里常见mp3的解码流,得到解码数据然后发送
  int mp3_decode_file_thread(void *d)
  {
#if MP3_EN
os_printf("%s:%d\n",__FUNCTION__,__LINE__);
    struct extern_read *libmad_read = (struct extern_read*)d;
    long flen, fsta, fend;
    int  dlen;
    mp3_file *mp3fp = NULL;
    stream *s = NULL;
    char *mp3_stream_name = (char*)custom_malloc_psram(64);
    os_sprintf(mp3_stream_name,"%s_%d",S_MP3_AUDIO,os_jiffies()%1000);
    s = open_stream_available(mp3_stream_name,8,0,opcode_func,mp3_stream_name);
    if(!s)
    {
        goto mp3_decode_file_thread_end;
    }
    os_printf("%s s:%X\n",__FUNCTION__,s);
    mp3fp = (mp3_file *)custom_malloc_psram(sizeof(mp3_file));
    mp3fp->mad_read = libmad_read;
    libmad_read->status = MP3_PLAY;

	
    flen = mp3fp->mad_read->readsize;

    if(flen<BUFSIZE)
    {
        mp3fp->fbsize = flen;
        mp3fp->fpos   = flen;
    }
    else
    {
        mp3fp->fbsize = BUFSIZE;
        mp3fp->fpos   = BUFSIZE;
    }

    mp3fp->flen   = flen;
    mp3fp->s = s;

    libmad_read->mp3_read(libmad_read,mp3fp->fbuf,mp3fp->fpos,mp3fp->fbsize);
    decode(mp3fp);

mp3_decode_file_thread_end:
    os_printf("%s:%d\n",__FUNCTION__,__LINE__);
    libmad_read->inside_status = MP3_STOP;
    libmad_read->mp3_close(libmad_read,NULL);
    if(mp3fp)
    {
        custom_free_psram(mp3fp);
    }
	
    if(s)
    {
        //关闭对应的流
        close_stream(s);
    }


    return 0;

#endif
  }



/*
 * This is a private message structure. A generic pointer to this structure
 * is passed to each of the callback functions. Put here any data you need
 * to access from within the callbacks.
 */


/*
 * This is the input callback. The purpose of this callback is to (re)fill
 * the stream buffer which is to be decoded. In this example, an entire file
 * has been mapped into memory, so we just call mad_stream_buffer() with the
 * address and length of the mapping. When this callback is called a second
 * time, we are finished decoding.
 */



static
enum mad_flow input(void *data,
		    struct mad_stream *stream)
{
  mp3_file *mp3fp;
  int      ret_code;
  uint32_t      unproc_data_size;    /*the unprocessed data's size*/
  int      copy_size;

  mp3fp = (mp3_file *)data;
  struct extern_read *mad_read = mp3fp->mad_read;
  //os_printf("mp3fp->fpos:%d\tflen:%d\n",mp3fp->fpos,mp3fp->flen);
  if(mp3fp->fpos-mp3fp->flen)
  {
      //os_printf("bufend:%X\tnext_frame:%X\n",stream->bufend,stream->next_frame);
      unproc_data_size = stream->bufend - stream->next_frame;
      //os_printf("unproc_data_size:%d\n",unproc_data_size);
      memcpy(mp3fp->fbuf, mp3fp->fbuf+mp3fp->fbsize-unproc_data_size, unproc_data_size);
      copy_size = BUFSIZE - unproc_data_size;
      if(mp3fp->fpos + copy_size > mp3fp->flen)
      {
          copy_size = mp3fp->flen - mp3fp->fpos;
      }
      mad_read->mp3_read(mad_read,mp3fp->fbuf+unproc_data_size,mp3fp->fpos,copy_size);
      //memcpy(mp3fp->fbuf+unproc_data_size,mp3fp->file_buf+mp3fp->fpos,copy_size);
      //osal_fread(mp3fp->fbuf+unproc_data_size, 1, copy_size, mp3fp->fp);
      mp3fp->fbsize = unproc_data_size + copy_size;
      mp3fp->fpos  += copy_size;
      
      /*Hand off the buffer to the mp3 input stream*/
      mad_stream_buffer(stream, mp3fp->fbuf, mp3fp->fbsize);
      ret_code = MAD_FLOW_CONTINUE;
  }
  else
  {
      ret_code = MAD_FLOW_STOP;
  }

  return ret_code;

}

/*
 * The following utility routine performs simple rounding, clipping, and
 * scaling of MAD's high-resolution samples down to 16 bits. It does not
 * perform any dithering or noise shaping, which would be recommended to
 * obtain any exceptional audio quality. It is therefore not recommended to
 * use this routine if high-quality output is desired.
 */

static inline
signed int scale(mad_fixed_t sample)
{
  /* round */
  sample += (1L << (MAD_F_FRACBITS - 16));

  /* clip */
  if (sample >= MAD_F_ONE)
    sample = MAD_F_ONE - 1;
  else if (sample < -MAD_F_ONE)
    sample = -MAD_F_ONE;

  /* quantize */
  return sample >> (MAD_F_FRACBITS + 1 - 16);
}

/*
 * This is the output callback function. It is called after each frame of
 * MPEG audio data has been completely decoded. The purpose of this callback
 * is to output (or play) the decoded PCM audio.
 */

static uint16_t CRC_16(uint8_t *temp,uint16_t len)
{
	uint16_t i,j;
	uint16_t CRC_1 = 0xFFFF;
	for(i = 0;i < len;i++)
	{
		CRC_1 ^= temp[i];
		for(j = 0;j < 8;j++)
		{
			if(CRC_1 & 0x01)
			{
				CRC_1 >>=1;
				CRC_1 ^= 0xA001;
			}
			else 
			{
				CRC_1 >>=1;
			}
		}
	}
	return(CRC_1);
}


static
enum mad_flow output(void *data,
		     struct mad_header const *header,
		     struct mad_pcm *pcm)
{
  unsigned int nchannels, nsamples;
  unsigned int rate;
  mad_fixed_t const *left_ch, *right_ch;
  mp3_file *mp3fp;
  struct data_structure  *s_data = NULL;
  uint32_t timeout = 0;
  
  mp3fp = (mp3_file *)data;
  struct extern_read *mad_read = mp3fp->mad_read;
  stream *s = mp3fp->s;
  uint16_t *buf = NULL;
  uint32_t offset = 0;
  int send_success = 0;
  /* pcm->samplerate contains the sampling frequency */

  rate= pcm->samplerate;
  nchannels = pcm->channels;
  nsamples  = pcm->length;
  left_ch   = pcm->samples[0];
  right_ch  = pcm->samples[1];
  //没有获取到帧,则要等待,否则就有可能错过解码数据
  //但要保证其他地方得到这个解码数据后,要迅速释放
  timeout = 0;
  while(!s_data && timeout < 1000)
  {
    s_data = get_src_data_f(s);
    os_sleep_ms(1);
    timeout++;
  }
  //os_printf("s_data:%X\n",s_data);
  buf = (uint16_t*)get_stream_real_data(s_data);
  
  //设置音频长度
  set_stream_real_data_len(s_data,nsamples*2);
  //_os_printf("left:%04X\tright:%04X\t%d\r\n",CRC_16((uint8_t*)left_ch,nsamples*2),CRC_16((uint8_t*)right_ch,nsamples*2),nsamples);
  while (nsamples--) {
    signed int sample;

    /* output sample(s) in 16-bit signed little-endian PCM */

    sample = scale(*left_ch++);
    //osal_fwrite(&sample,1,2,pcm_fp);
    buf[offset++] = sample;


    if (nchannels == 2) {
      sample = scale(*right_ch++);
      //osal_fwrite(&sample,1,2,pcm_fp);


    }

  }

    //音频发送
    timeout = 0;
    hold_data(s_data);
    while(!send_success && timeout < 1000)
    {
        //这里判断一下采样率是不是正常的,但不支持mp3动态修改采样率
        if(mad_read->audio_hz != rate)
        {
            mad_read->audio_hz = rate;
            broadcast_cmd_to_destStream(s,SET_CMD_TYPE(CMD_AUDIO_DAC_MODIFY_HZ,rate));
        }
        send_success = send_data_to_stream(s_data);
        os_sleep_ms(1);
        timeout++;
    }
    free_data(s_data);
    s_data = NULL;
    //这里检查一下状态
    if(mad_read->status == MP3_PAUSE)
    {
        printf("%s pause\n",__FUNCTION__);
        mad_read->inside_status = mad_read->status;
        while(mad_read->status == MP3_PAUSE)
        {
            os_sleep_ms(1);
        }
        mad_read->inside_status = MP3_PLAY;
    }

    //判断一下是否要停止
    //这里检查一下状态
    if(mad_read->status == MP3_STOP)
    {
        printf("%s stop\n",__FUNCTION__);
        //mad_read->inside_status = mad_read->status;
        return MAD_FLOW_STOP;
    }

  return MAD_FLOW_CONTINUE;
}


/*
 * This is the error callback function. It is called whenever a decoding
 * error occurs. The error is indicated by stream->error; the list of
 * possible MAD_ERROR_* errors can be found in the mad.h (or stream.h)
 * header file.
 */

static enum mad_flow error(void *data,
		    struct mad_stream *stream,
		    struct mad_frame *frame)
{
  mp3_file *mp3fp = data;

  os_printf("decoding error 0x%04x (%s) at byte offset %u\n",
	  stream->error, mad_stream_errorstr(stream),
	  stream->this_frame - mp3fp->fbuf);

  /* return MAD_FLOW_BREAK here to stop decoding (and propagate an error) */

  return MAD_FLOW_CONTINUE;
}

/*
 * This is the function called by main() above to perform all the decoding.
 * It instantiates a decoder object and configures it with the input,
 * output, and error callback functions above. A single call to
 * mad_decoder_run() continues until a callback function returns
 * MAD_FLOW_STOP (to stop decoding) or MAD_FLOW_BREAK (to stop decoding and
 * signal an error).
 */

static int decode(mp3_file *mp3fp)
{
  struct mad_decoder decoder;
  int result;
  //pcm_fp = osal_fopen("2.pcm","wb");
  /* configure input, output, and error functions */
  mad_decoder_init(&decoder, mp3fp,
		   input, 0 /* header */, 0 /* filter */, output,
		   error, 0 /* message */);

  /* start decoding */
  result = mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);

  /* release the decoder */
  mad_decoder_finish(&decoder);
  //osal_fclose(pcm_fp);
  return result;
}



static struct extern_read *g_mp3_decode = NULL;

//需要根据MP3的读取长度配置
#define SD_TMP_SIZE (4096)


//这里只是读取一次文件,如果长度不对,不
uint32_t mp3_read_func(struct extern_read *e_read,void *ptr,uint32_t offset,uint32_t size)
{
	void *fp = e_read->fp;
    uint32_t len;
    uint32_t read_size = SD_TMP_SIZE;
    uint32_t read_offset = 0;
	//os_printf("read size:%d\tptr:%X\n",size,ptr);
    while(size)
    {
        if(size > SD_TMP_SIZE)
        {
            len = osal_fread(e_read->sd_buf,1,SD_TMP_SIZE,fp);
            read_size += SD_TMP_SIZE;
        }
        else
        {
            len = osal_fread(e_read->sd_buf,1,size,fp);
            read_size = size;
        }
        memcpy(ptr+read_offset,e_read->sd_buf,size);
        read_offset += read_size;
        size -= read_size;
    }

	return read_offset;
}

void mp3_close_func(struct extern_read *e_read)
{
	osal_fclose(e_read->fp);
    custom_free(e_read->sd_buf);
    e_read->fp = NULL;
    g_mp3_decode = NULL;
    custom_free_psram(e_read);
}

void *mp3_read_init(char *filename)
{
    //已经有使用该mp3解码,需要先等待解码结束才可以继续,可以去读取状态或者控制状态
    if(g_mp3_decode)
    {
        return NULL;
    }
	struct extern_read *e_read = (struct extern_read*)custom_malloc_psram(sizeof(struct extern_read));
    memset(e_read,0,sizeof(struct extern_read));
	e_read->fp = osal_fopen(filename,"rb");
	os_printf("e_read->fp:%X\n",e_read->fp);
	e_read->readsize = osal_fsize(e_read->fp);
	os_printf("e_read->readsize:%d\n",e_read->readsize);
	e_read->mp3_read = mp3_read_func;
	e_read->mp3_close = mp3_close_func;
    //注意,这个要用sram的空间
    e_read->sd_buf = (uint8_t*)custom_malloc(SD_TMP_SIZE);
    //sd卡临时buf申请
    g_mp3_decode = e_read;
	return (void*)e_read;
}

//读取当前mp3解码的状态,如果播放之前,最好先判断一下
uint8_t get_mp3_decode_status()
{
    //os_printf("g_mp3_decode:%X\n",g_mp3_decode);
    if(g_mp3_decode)
    {
        //os_printf("g_mp3_decode->inside_status:%d\n",g_mp3_decode->inside_status);
        return g_mp3_decode->inside_status;
    }
    return MP3_STOP;
}

//外部调用,可以控制mp3解码状态,但不是立刻生效,应该是等待解码下一帧开始生效
uint8_t set_mp3_decode_status(uint8_t status)
{
    if(g_mp3_decode)
    {
        g_mp3_decode->status = status; 
    }
    return 1;
}

/***********************************************************************************
 * 说明:尽量调用逻辑是,先判断状态,如果mp3解码已经停止了,则可以新建一个线程去重新解码mp3解码
 * get_mp3_decode_status();
 * mp3_decode_thread("1.mp3");
*********************************************************************************/
void mp3_decode_thread(void *d)
{
    uint32_t count = 0;
    uint8_t status = get_mp3_decode_status();
    if(status != MP3_STOP)
    {
        set_mp3_decode_status(MP3_STOP);
    }
    while((get_mp3_decode_status() != MP3_STOP) && count < 1000)
    {
        os_sleep_ms(1);
        count ++;
    }
    if(count >= 1000)
    {
        os_printf("%s creat fail:%d\n",__FUNCTION__,get_mp3_decode_status());
        return;
    }
    
    const char *filename = (const char *)d;

    void *mp3 = mp3_read_init(filename);
    if(mp3)
    {
        k_task_handle_t eloop_task_handle;
        csi_kernel_task_new((k_task_entry_t)mp3_decode_file_thread, "libmad_test", mp3, 20, 0, NULL, 8192, &eloop_task_handle);
    }
}